package com.lzwork.gpt.utility.batchjob;

import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;

import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson2.JSONArray;
import com.lzwork.utility.data.TypeEnum;
import com.lzwork.utility.file.FileUtility;
import com.lzwork.utility.logger.LOGGER;
import com.lzwork.utility.str.StrUtility;
import com.lzwork.web.BaseController;
import com.lzwork.web.WebResultCode;
import com.lzwork.web.data.BoolResultInfo;

/**
 * @author LiZheng
 * @date 2024年7月31日 09:46:51
 */

public abstract class BatchJobController extends BaseController
{
	protected abstract BatchJobManager manager();

	protected SimpleDateFormat dateFormat = new SimpleDateFormat(StrUtility.DEF_DATE_FORMAT_SIMPLE);
	protected SimpleDateFormat dateNumFormat = new SimpleDateFormat(StrUtility.DEF_DATE_FORMAT_FULLNUM);

	@GetMapping(value = "/info")
	@ResponseBody
	public JSONObject info(@RequestParam(value = "fullInfo", defaultValue = "false") Boolean fullInfo,
			@RequestParam(value = "mSort", defaultValue = "0") int mSort,
			@RequestParam(value = "username", defaultValue = "") String username)
	{
		JSONObject result = new JSONObject();

		List<SingleJob> allJob = manager().getAllJob(mSort, username);

		List<JSONObject> objs = new ArrayList<>();
		for (SingleJob e : allJob)
		{
			objs.add(e.info());
		}
		result.put("jobs", objs);

		if (StrUtility.stringValid(username))
		{
			boolean hasUser = manager().hasUser(username);
			String userInfo = hasUser ? "User name: [" + username + "]" : "User not found: [" + username + "]";
			result.put("user", userInfo);
		}

		return WebResultCode.SUCCESS.toDataJobj(result);
	}

	@GetMapping(value = "/allTypes")
	@ResponseBody
	public JSONObject allTypes()
	{
		List<TypeEnum> types = manager().getAllJobTypes();

		JSONArray typeData = new JSONArray();

		for (TypeEnum te : types)
		{
			typeData.add(te.toJObj());
		}

		return WebResultCode.SUCCESS.toDataJobj(typeData);
	}

	@GetMapping(value = "/itemInfo")
	@ResponseBody
	public JSONObject itemInfo(@RequestParam String id)
	{
		JSONObject result = new JSONObject();

		SingleJob job = manager().getJob(id);
		if (job == null)
			return WebResultCode.PARAMETER_INVALID.toErrJobj("任务 [" + id + "] 不存在");

		result.put("job", job.info());

		return WebResultCode.SUCCESS.toDataJobj(result);
	}

	@PostMapping(value = "createJob", produces = "application/json;charset=UTF-8")
	@ResponseBody
	public JSONObject createJob(HttpServletRequest request, @RequestParam("dataFile") MultipartFile dataFile,
			@RequestParam(value = "type") int type, @RequestParam(value = "username") String username,
			@RequestParam(value = "rename", defaultValue = "") String rename)
	{
		JSONObject result = new JSONObject();

		String fileName = dataFile.getOriginalFilename();
		if (!StrUtility.stringValid(fileName))
		{
			return WebResultCode.FILE_SAVE_ERROR.toErrJobj("Get file fail. Empty file name.");
		}

		long tick = BatchJobManager.getTick();

		Date date = new Date(tick);
		String perfix = dateNumFormat.format(date);

		TypeEnum jobType = manager().parseType(type);

		if (jobType == null)
			return WebResultCode.PARAMETER_INVALID.toErrJobj("Invalid job type: " + type);

		try
		{
			String saveName = perfix + "@" + fileName;

			File saved = FileUtility.ajaxFileSave(dataFile, FileUtility.getFileSafe(manager().tempFolder(), saveName));
			LOGGER.log(LOGGER.APP, "Upload data file saved to :" + saved.getAbsolutePath());

			if (saved.exists())
			{
				BoolResultInfo info = manager().createJob(saved, jobType, username, tick, rename);

				if (info.isDone())
				{
					JSONObject o = (JSONObject) info.rawInfo();
					result.put("result", o);
					String id = o.getString("id");

					JSONObject subParam = null;

					String paramStr = getHeaderParam(request, "option");
					if (StrUtility.stringValid(paramStr))
					{
						try
						{
							subParam = JSONObject.parseObject(paramStr);
						} catch (Exception e)
						{
							subParam = null;
							result.put("info", "Parse option data fail.");
						}

						if (subParam != null)
						{
							SingleJob job = manager().getJob(id);
							BoolResultInfo optInfo = job.setOptionData(subParam);
							if (optInfo.isDone())
							{
								result.put("info", "Option set:" + optInfo.info());
							}
						}
					}
				} else
					return WebResultCode.UNKNOWN_ERROR.toErrJobj(info.info());
			} else
				return WebResultCode.FILE_SAVE_ERROR.toErrJobj("Get file fail.");

		} catch (IOException e)
		{
			String err = exceptionInfo(e);
			return WebResultCode.MQ_MESSAGE_ERROR.toErrJobj(err);
		}

		return WebResultCode.SUCCESS.toDataJobj(result);
	}

	@GetMapping(value = "/beginJob")
	@ResponseBody
	public JSONObject beginJob(@RequestParam String id)
	{
		if (!StrUtility.stringValid(id))
			return WebResultCode.PARAMETER_INVALID.toErrJobj("无效的任务ID");

		BoolResultInfo info = manager().beginJob(id);

		if (info.isDone())
			return WebResultCode.SUCCESS.toJobj();

		return WebResultCode.PARAMETER_INVALID.toErrJobj(info.info());
	}

	@PostMapping(value = "setJobOpt", produces = "application/json;charset=UTF-8")
	@ResponseBody
	public JSONObject setJobOpt(@RequestBody Map<?, ?> map)
	{
		String id = getMapStr(map, "id");

		if (!StrUtility.stringValid(id))
			return WebResultCode.PARAMETER_INVALID.toErrJobj("无效的任务ID");

		SingleJob job = manager().getJob(id);
		if (job == null)
			return WebResultCode.PARAMETER_INVALID.toErrJobj("任务 [" + id + "] 不存在");

		BoolResultInfo cr = job.setOptionData(map);
		if (cr.isDone())
			return WebResultCode.SUCCESS.toDataJobj("Option param changed: " + cr.info());

		return WebResultCode.PARAMETER_INVALID.toErrJobj(cr.info());
	}

	@GetMapping(value = "/getJobOpt")
	@ResponseBody
	public JSONObject getJobOpt(@RequestParam String id,
			@RequestParam(value = "fullInfo", defaultValue = "false") Boolean fullInfo)
	{
		if (!StrUtility.stringValid(id))
			return WebResultCode.PARAMETER_INVALID.toErrJobj("无效的任务ID");

		SingleJob job = manager().getJob(id);
		if (job == null)
			return WebResultCode.PARAMETER_INVALID.toErrJobj("任务 [" + id + "] 不存在");

		JobOption option = job.getOption();

		return WebResultCode.SUCCESS.toDataJobj(option.getParamInfo(fullInfo));
	}

	@GetMapping(value = "/getLog")
	@ResponseBody
	public JSONObject getLog(@RequestParam String id)
	{
		if (!StrUtility.stringValid(id))
			return WebResultCode.PARAMETER_INVALID.toErrJobj("无效的任务ID");

		SingleJob job = manager().getJob(id);
		if (job == null)
			return WebResultCode.PARAMETER_INVALID.toErrJobj("任务 [" + id + "] 不存在");

		JSONObject obj = new JSONObject();
		obj.put("log", job.getLog());

		return WebResultCode.SUCCESS.toDataJobj(obj);
	}

	@GetMapping(value = "/delJob")
	@ResponseBody
	public JSONObject delJob(@RequestParam String id, String confirm, @RequestParam Integer type)
	{
		if (!StrUtility.stringValid(true, id, confirm))
			return WebResultCode.PARAMETER_INVALID.toErrJobj("无效的任务ID");

		if (!id.equals(confirm))
			return WebResultCode.PARAMETER_INVALID.toErrJobj("任务ID 校验失败");
		if (type == null)
			return WebResultCode.PARAMETER_INVALID.toErrJobj("操作类型不可为空");

		BoolResultInfo info;
		if (type == 1)
			info = manager().delJob(id);
		else if (type == 2)
			info = manager().resetJob(id);
		else if (type == 999) // TODO: force delete
			info = manager().delJob(id);
		else
			return WebResultCode.PARAMETER_INVALID.toErrJobj("未知的操作类型：" + type);

		if (info.isDone())
			return WebResultCode.SUCCESS.toJobj();

		return WebResultCode.UNKNOWN_ERROR.toErrJobj(info.info());
	}

	@GetMapping(value = "downloadFile", produces = "application/json;charset=UTF-8")
	public void fileTrans(HttpServletRequest request, HttpServletResponse response, @RequestParam String id,
			@RequestParam Integer type) throws IOException
	{
		SingleJob job = manager().getJob(id);

		if (job == null)
			throwCode200(response, "Invalid job id: " + id);

		File path = type == 0 ? job.getSource() : job.getOutputFile();

		if (path == null)
			throwCode200(response, "Null " + (type == 0 ? "source " : "Output ") + "file for id: " + id);

		doFileTrans(response, type, path, false);
	}
}
